#pragma once
#include "Socket.hpp"
#include <sys/epoll.h>
class EpollServer
{
    const static int size = 128;
    const static int num = 128;

public:
    EpollServer(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        _listensock->BuildListenSocket();
        _epfd = ::epoll_create(size);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll create error\n");
            exit(1);
        }
        LOG(INFO, "epoll create success\n");
    }
    void InitServer()
    {
        // 添加listensock到epoll里
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listensock->sockfd();
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->sockfd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "epoll ctl error\n");
            exit(2);
        }
        LOG(INFO, "epoll ctl success,add new sockfd %d\n", _listensock->sockfd());
    }
    void Loop()
    {
        int timeout = 1000;
        while (1)
        {
            int n = ::epoll_wait(_epfd, revs, num, timeout);
            switch (n)
            {
            case 0:
            {
                LOG(INFO, "epoll timeout\n");
                break;
            }
            case -1:
            {
                LOG(ERROR, "epoll error\n");
                break;
            }
            default:
            {
                LOG(INFO, "haved event happend,%d\n", n);
                HandleEvent(n);
                break;
            }
            }
        }
    }
    string EventsToString(uint32_t events)
    {
        string eventstr;
        if (events & EPOLLIN)
            eventstr += "EPOLLIN ";
        else if (events & EPOLLOUT)
            eventstr += "EPOLLOUT ";
        return eventstr;
    }
    void HandleAccpet()
    {
        InetAddr addr;
        int sockfd = _listensock->Accepter(&addr);
        if (sockfd < 0)
        {
            LOG(ERROR, "accept error\n");
            return;
        }
        LOG(INFO, "get a new link %d %s:%d\n", sockfd, addr.Ip().c_str(), addr.Port());
        // 把新的添加到epoll里面，就绪就可以读写了
        struct epoll_event ev;
        ev.data.fd = sockfd;
        ev.events = EPOLLIN;
        int ret = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        if (ret < 0)
        {
            LOG(FATAL, "epoll ctl error\n");
        }
        else
            LOG(INFO, "epoll ctl success,add new sockfd %d\n", sockfd);
    }
    void HandleRecv(int fd)
    {
        char buff[4096];
        int ret = ::recv(fd, buff, sizeof(buff) - 1, 0);
        if (ret > 0)
        {
            buff[ret] = 0;
            cout << buff << endl;
            string s = buff;
            ::send(fd, s.c_str(), s.size(), 0);
        }
        else if (ret == 0)
        {
            LOG(INFO, "client quit,close %d\n", fd);
            // 把fd关闭，从epoll移除该节点
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
        else
        {
            LOG(ERROR, "client recv error,fd:%d\n", fd);
            // 把fd关闭，从epoll移除该节点
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            ::close(fd);
        }
    }
    void HandleEvent(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int fd = revs[i].data.fd;
            uint32_t revents = revs[i].events;
            LOG(INFO, "fd:%d events:%s ok\n", fd, EventsToString(revents).c_str());
            if (revents & EPOLLIN)
            {
                if (fd == _listensock->sockfd())
                {
                    HandleAccpet();
                }
                else
                {
                    HandleRecv(fd);
                }
            }
        }
    }
    ~EpollServer()
    {
        if (_epfd >= 0)
            ::close(_epfd);
        _listensock->Close();
    }

private:
    uint16_t _port;
    std::unique_ptr<TcpSocket> _listensock;
    int _epfd;
    struct epoll_event revs[num];
};